package com.rocbin.quartz.trigger;

import org.quartz.*;
import org.quartz.impl.triggers.AbstractTrigger;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;

/**
 * 保留已经结束的 Trigger 包装类
 *
 * @author Rocbin 2016
 *         Created by Rocbin on 2016/11/8.
 * @version 1.0
 */
public class NeedRetainTriggerSupport extends AbstractTrigger {

    private AbstractTrigger triggerSupport;

    private transient Method validateMisfireInstruction;
    private transient Method validate;

    // 参考http://www.ibm.com/developerworks/cn/opensource/os-cn-quartz/
    private boolean needRetain = true;


    public AbstractTrigger getTriggerSupport() {
        return triggerSupport;
    }

    public boolean isNeedRetain() {
        return needRetain;
    }

    public NeedRetainTriggerSupport setNeedRetain(boolean isNeedRetain) {
        this.needRetain = isNeedRetain;
        return this;
    }

    public NeedRetainTriggerSupport() {
    }

    public NeedRetainTriggerSupport(AbstractTrigger triggerSupport) {
        this.triggerSupport = triggerSupport;
        String jobName = triggerSupport.getJobName();
        if (jobName == null || jobName.trim().length() == 0) {
            throw new IllegalArgumentException("NeedRetainTriggerSupport: your trigger jobName cannot be blank! help: before calling trigger.setJobName(helloJob)");
        }
    }

    @Override
    public CompletedExecutionInstruction executionComplete(JobExecutionContext context, JobExecutionException result) {
        if (result != null && result.refireImmediately()) {
            return CompletedExecutionInstruction.RE_EXECUTE_JOB;
        }

        if (result != null && result.unscheduleFiringTrigger()) {
            return CompletedExecutionInstruction.SET_TRIGGER_COMPLETE;
        }

        if (result != null && result.unscheduleAllTriggers()) {
            return CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_COMPLETE;
        }

        if (!mayFireAgain() && !needRetain) {
            return CompletedExecutionInstruction.DELETE_TRIGGER;
        }

        return CompletedExecutionInstruction.NOOP;
    }

    @Override
    public TriggerKey getKey() {
        return triggerSupport.getKey();
    }

    @Override
    public JobKey getJobKey() {
        return triggerSupport.getJobKey();
    }

    @Override
    public String getDescription() {
        return triggerSupport.getDescription();
    }

    @Override
    public String getCalendarName() {
        return triggerSupport.getCalendarName();
    }

    @Override
    public JobDataMap getJobDataMap() {
        return triggerSupport.getJobDataMap();
    }

    @Override
    public int getPriority() {
        return triggerSupport.getPriority();
    }

    @Override
    public void triggered(Calendar calendar) {
        triggerSupport.triggered(calendar);
    }

    @Override
    public Date computeFirstFireTime(Calendar calendar) {
        return triggerSupport.computeFirstFireTime(calendar);
    }

    @Override
    public boolean mayFireAgain() {
        return triggerSupport.mayFireAgain();
    }

    @Override
    public Date getStartTime() {
        return triggerSupport.getStartTime();
    }

    @Override
    public void setStartTime(Date startTime) {
        triggerSupport.setStartTime(startTime);
    }

    @Override
    public void setEndTime(Date endTime) {
        triggerSupport.setEndTime(endTime);
    }

    @Override
    public Date getEndTime() {
        return triggerSupport.getEndTime();
    }

    @Override
    public Date getNextFireTime() {
        return triggerSupport.getNextFireTime();
    }

    @Override
    public Date getPreviousFireTime() {
        return triggerSupport.getPreviousFireTime();
    }

    @Override
    public Date getFireTimeAfter(Date afterTime) {
        return triggerSupport.getFireTimeAfter(afterTime);
    }

    @Override
    public Date getFinalFireTime() {
        return triggerSupport.getFinalFireTime();
    }

    @Override
    protected boolean validateMisfireInstruction(int candidateMisfireInstruction) {
        try {
            Method validateMisfireInstruction = getTriggerSupportValidateMisfireInstructionMethod();
            return (boolean) validateMisfireInstruction.invoke(triggerSupport, candidateMisfireInstruction);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    private synchronized Method getTriggerSupportValidateMisfireInstructionMethod() throws NoSuchMethodException {
        if (validateMisfireInstruction == null) {
            validateMisfireInstruction = triggerSupport.getClass().getDeclaredMethod("validateMisfireInstruction", int.class);
            validateMisfireInstruction.setAccessible(true);
        }
        return validateMisfireInstruction;
    }

    @Override
    public void validate() throws SchedulerException {
        try {
            Method validate = getTriggerSupportValidateMethod();
            validate.invoke(triggerSupport);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            if (e.getCause() instanceof SchedulerException) {
                throw ((SchedulerException) e.getCause());
            } else {
                throw new RuntimeException(e);
            }
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    private synchronized Method getTriggerSupportValidateMethod() throws NoSuchMethodException {
        if (validate == null) {
            validate = triggerSupport.getClass().getDeclaredMethod("validate");
        }
        return validate;
    }

    @Override
    public int getMisfireInstruction() {
        return triggerSupport.getMisfireInstruction();
    }

    @Override
    public void updateAfterMisfire(Calendar cal) {
        triggerSupport.updateAfterMisfire(cal);
    }

    @Override
    public void updateWithNewCalendar(Calendar cal, long misfireThreshold) {
        triggerSupport.updateWithNewCalendar(cal, misfireThreshold);
    }

    @Override
    public void setNextFireTime(Date nextFireTime) {
        triggerSupport.setNextFireTime(nextFireTime);
    }

    @Override
    public void setPreviousFireTime(Date previousFireTime) {
        triggerSupport.setPreviousFireTime(previousFireTime);
    }

    @Override
    public TriggerBuilder<? extends Trigger> getTriggerBuilder() {
        return triggerSupport.getTriggerBuilder();
    }

    @Override
    public ScheduleBuilder<? extends Trigger> getScheduleBuilder() {
        return triggerSupport.getScheduleBuilder();
    }

    @Override
    public int compareTo(Trigger other) {
        return triggerSupport.compareTo(other);
    }
}
